<!--
查询表单，一般配合表格使用
最后修改：20220225
作者：ljy
一、用法
（一）基本用法
1.1 引入组件的方式
import TableQuery from '@/components/TableQuery.vue';
1.2 使用方式
<TableQuery
      :query="query"
      :loadTableList="loadTableList"
      :clearQuery="clearQuery"
      :queryForm="queryForm"
    />
1.3 参数说明：
(1)query、loadTableList和clearQuery的详细说明参照文件'@/api/uem/useTableList.ts'，为表格封装的必备元素
(2)queryForm是用来组成表单内元素的数组
const queryForm=[
  { // 文本框
    prop: 'username',// 传递参数的model名字
    label: '账号', //表单的label信息
    placeholder:'请输入账户名', //可选：表单内元素的placeholder提示信息，如果缺省该条件，则表单元素的placeholder提示信息默认为是label
  },
  { // 文本框
    prop: 'nickName',
    label: '网名'
  },
  { // select 下拉框
    prop: 'sex',
    label: '性别',
    placeholder:'请选择性别',
    list: [
      {
        value: 0, // 对应option的value属性
        label: '未选择' // 对应option的label属性
      },
      {
        value: 1,
        label: '男'
      },
      {
        value: 2,
        label: '女'
      }
    ]
  }
  ];
（二)查询表单插槽
2.1 说明
如果queryForm不能满足全部的查询表单要求，则可以通过插槽来定制开发
2.2.用法
<TableQuery
  :query="query"
  :loadTableList="loadTableList"
  :clearQuery="clearQuery"
  :queryForm="queryForm"
>
  <template #left>
    <el-form-item label="性别">
      <el-input v-model="query.sex"></el-input>
    </el-form-item>
  </template>
  <template #right>
    <el-form-item label="状态">
      <el-input v-model="query.status"></el-input>
    </el-form-item>
  </template>
</TableQuery>
2.3 标签说明
（1）<template #left></template>
主要用于生成在queryForm渲染生成的表单元素左边
（2）<template #right></template>
主要用于生成在queryForm渲染生成的表单元素右边，在按钮左边。
（3）插槽中v-model绑定的参数都应该用query开头，如果复杂点可以如下：query.user.sex

-->
<template>
  <!--搜索栏-->
  <!--说明：加上@submit.prevent是为了解决当表单中只有一个元素时，直接在查询表单回车会直接刷新页面的BUG。-->
  <el-form
    v-if="formHeaders.length > 0"
    :model="queryForm"
    :inline="true"
    style="text-align: left"
    class="demo-form-inline"
    ref="queryFormRef"
    @submit.prevent
    @keyup.enter="submitQuery"
  >
    <!--表单左边的插槽-->
    <slot name="left" :data="queryForm"></slot>
    <template v-for="(item, index) in formHeadersVisible" :key="index">
      <el-form-item :label="item.label">
        <!--自定义插槽，在父组件自行进行创建-->
        <span v-if="item.type === 'custom'">
          <slot :name="item.prop" v-bind:item="item"> </slot>
        </span>
        <!--如果是普通选择框,设计宽度-->
        <el-select
          v-else-if="item.type === 'select' && item.list && item.list.length"
          v-model="queryForm[item.prop]"
          :multiple="item.config ? (item.config.multiple ? item.config.multiple : false) : false"
          :multiple-limit="item.config ? (item.config.multipleLimit ? item.config.multipleLimit : 0) : 0"
          clearable
          :placeholder="`${item.placeholder ? item.placeholder : item.label}`"
          :style="`width:${item.width ? item.width : '200px'}`"
          @change="autoSubmitQuery"
        >
          <el-option v-for="(item1, index1) in item.list" :key="index1" :label="item1.label" :value="item1.value"></el-option>
        </el-select>
        <!--如果是下拉框中的数据是对象。item.config.valueKey不传值则默认是id属性-->
        <el-select
          v-else-if="item.type === 'selectObject' && item.list && item.list.length > 0 && item.config && item.config.defaultProp"
          v-model="queryForm[item.propAsName ? item.propAsName : item.prop]"
          :value-key="item.config.valueKey ? item.config.valueKey : 'id'"
          :multiple="item.config.multiple ? item.config.multiple : false"
          :multiple-limit="item.config.multipleLimit ? item.config.multipleLimit : 0"
          clearable
          :placeholder="item.placeholder ? item.placeholder : item.label"
          :style="`width:${item.width ? item.width : '200px'}`"
          @change="autoSubmitQuery"
        >
          <el-option
            v-for="(item1, index1) in item.list"
            :key="index1"
            :label="`${item.config.defaultProp.label ? item1[item.config.defaultProp.label] : 'name'}`"
            :value="`${item.config.defaultProp.value ? item1[item.config.defaultProp.value] : 'id'}`"
          ></el-option>
        </el-select>
        <!--远程加载文本框-->
        <RemoteElSelectComponent
          v-else-if="item.type === 'selectRemote' && item.config && item.config.defaultProp"
          v-model="queryForm[item.propAsName ? item.propAsName : item.prop]"
          :url="item.url!"
          :propValue="item.config.defaultProp.value ? item.config.defaultProp.value : 'id'"
          :propLabel="item.config.defaultProp.label ? item.config.defaultProp.label : 'name'"
          :propLabelRegexType="item.config.defaultProp.labelRegexType ? item.config.defaultProp.labelRegexType : 2"
          :multiple="item.config.multiple ? item.config.multiple : false"
          :multiple-limit="item.config.multipleLimit ? item.config.multipleLimit : 0"
          :queryName="item.queryNameForUrl!"
          clearable
          :placeholder="item.placeholder ? item.placeholder : item.label"
          :width="item.width"
          @change="autoSubmitQuery"
        />
        <!--如果是日期选择框-一段时间,设计宽度-->
        <!--注意，如果v-model的值采用普通表单元素，使用queryForm[item.prop]来定义，则会因为绑定的元素的一个数组，导致上传后端接口时报错
        例如：createQueryTimeBetween[]: 2024-08-15 00:00:00;createQueryTimeBetween[]: 2024-09-19 00:00:00。
        因此需要先利用其在组件先做一个变量初始化，才能使用.
        20240821:为了更好解决该组件的兼容问题，将其设置为一个单独组件后，完美解决了组件的赋值和清空问题。
        -->
        <ELDatePickerOfDateRangeComponent
          v-else-if="item.type === 'daterange' && item.propAsNames && item.propAsNames.length === 2"
          v-model="queryFormInit[item.prop]"
          v-model:timeBegin="queryForm[item.propAsNames[0]]"
          v-model:timeEnd="queryForm[item.propAsNames[1]]"
          @change="autoSubmitQuery"
        />
        <el-input
          v-else
          v-model="queryForm[item.prop]"
          :placeholder="`${item.placeholder ? item.placeholder : item.label}`"
          :style="`width:${item.width ? item.width : '200px'}`"
          :readonly="item.readonly"
          :disabled="item.disabled"
          clearable
          @input="autoSubmitQuery"
        />
      </el-form-item>
    </template>
    <!--      <el-form-item label="账户名">
        <el-input v-model="query.username"></el-input>
      </el-form-item>-->
    <!--表单右边的插槽-->
    <slot name="right" :data="queryForm"> </slot>
    <slot name="submit" :data="queryForm">
      <el-form-item>
        <TableQueryFormItemSetting :formHeaders="formHeaders" :extraFormHeaders="extraFormHeaders" @changeSelect="changeSelect" />
        <!-- 如果查询字段都隐藏了，则不显示下面按钮 -->
        <template v-if="formHeadersVisible.length > 0">
          <el-button type="primary" @click="submitQuery">查询</el-button>
        </template>
        <el-button type="primary" v-show="showResetButton" @click="clearAndRereshTable">重置</el-button>
      </el-form-item>
    </slot>
  </el-form>
</template>

<script lang="ts" setup>
import TableQueryFormItemSetting from '@/components/base/table/TableQueryFormItemSetting.vue';
import ELDatePickerOfDateRangeComponent from '@/components/base/form/ELDatePickerOfDateRangeComponent.vue';
import RemoteElSelectComponent from '@/components/base/form/RemoteElSelectComponent.vue';
import { ref, computed, watch, nextTick } from 'vue';
import type { FormHeader } from '@/types/interface/base_interface';
import type { FormRules } from 'element-plus';
const emit = defineEmits(['beforeClearQuery', 'afterClearQuery', 'beforeSubmitQuery']);

interface Props {
  // 需要在父组件引入自定义的@/api/uem/useTableList.ts组件，然后将其中的部分属性传过来
  loadTableList: () => void; // 重新加载表格
  clearQuery: () => void; // 当页面发生变化时触发的一个函数
  removeQueryField: (key: string) => void; // 移除查询条件中的某个元素
  queryFormHeaders: Array<FormHeader>; // 查询表单
  showResetButton?: Boolean; //是否显示重置按钮
  autoSubmit?: Boolean; //自动提交，如果是true则表示表单值修改后会自动执行提交动作
}
const props = withDefaults(defineProps<Props>(), {
  queryFormHeaders: () => new Array<FormHeader>(),
  showResetButton: () => Boolean(true),
  autoSubmit: () => Boolean(false)
});

// 查询组件表单对象。
const queryForm = defineModel<Record<string, any>>('queryForm', {
  required: true,
  default: {}
});

// 初始化时显示的表单元素
const formHeaders = ref<FormHeader[]>([]);
// 需要手动操作显示的表单元素
const extraFormHeaders = ref<FormHeader[]>([]);

//表单初始化信息
const queryFormInit = ref<any>({});
// 对表单的默认显示状态进行处理
// 初始化表单（因表单选项可能通过异步交互获取，20240819这里改为watch监听）
watch(
  () => props.queryFormHeaders,
  (newVal: any, oldVal: any) => {
    if (typeof newVal !== 'undefined' && newVal.length > 0) {
      //先清空原来表单的基本元素和额外元素
      formHeaders.value.splice(0, formHeaders.value.length);
      extraFormHeaders.value.splice(0, extraFormHeaders.value.length);
      //再获取原来表单的基本元素和额外元素
      props.queryFormHeaders.forEach((item: FormHeader) => {
        if (item.show === false) {
          // 如果不显示，则作为扩展表单
          extraFormHeaders.value.push(item);
        } else {
          formHeaders.value.push(item);
        }
      });
    }
  },
  { deep: true, immediate: true }
);

// const formHeaders = computed(() => JSON.parse(JSON.stringify(props.queryFormHeaders)));
// 实现查询表单的动态显示
const formHeadersVisible = ref<FormHeader[]>([]); // 定义用于显示的列
// 当列选择发生变化时，查询表单的显示数据也发生变化
const changeSelect = (columnsSelected: string[]) => {
  formHeadersVisible.value = [];
  props.queryFormHeaders.forEach((formHeader: FormHeader) => {
    if (columnsSelected.indexOf(formHeader.prop) !== -1) {
      formHeadersVisible.value.push(formHeader);
    }
  });
};

// 提交查询表单
// 说明：只提交显示的表单
const submitQuery = async () => {
  emit('beforeSubmitQuery', queryForm.value);
  // 将查询表单中已经隐藏的属性移除
  //下面是遍历查询表单中已经赋值的所有元素
  Object.keys(queryForm.value).forEach((key: string) => {
    let result = false; // 默认该主键在查询表单中不存在
    for (let i = 0; i < formHeadersVisible.value.length; i += 1) {
      let formHeader: FormHeader = formHeadersVisible.value[i];
      //这里移除的判断存在缺陷：例如某组件，如时间区间选择插件，1个组件会绑定1个数组（包含2个表单对象），这样在这里都无法判定为存在
      if (key === (formHeader.propAsName ? formHeader.propAsName : formHeader.prop)) {
        if (Array.isArray(formHeader.propAsNames) && formHeader.propAsNames.length > 0) {
          //如果这个主属性的propAsNames有值，则该主属性无效。例如时间区间选择插件绑定的值（数组）是不能直接作为查询条件的
          result = true;
          break;
        } else {
          // propAsName针对关联属性命名移除的判断，如department.id
          result = true;
          break;
        }
      } else {
        //如果key值不存在于直接指定显示的树形中
        //如果是propAsNames存在，即一个组件绑定多个属性。如日期区间组件
        if (Array.isArray(formHeader.propAsNames) && formHeader.propAsNames.length > 0) {
          //遍历每个属性
          for (let propNameOfEach of formHeader.propAsNames) {
            if (key === propNameOfEach) {
              result = true;
              break;
            }
          }
        }
      }
    }
    if (result === false) {
      //但要注意一点，就是如果该属性的值是通过其他值得到，那么即便这里删除了，值还会存在（因为中间值还存在）。
      //因此要建立和中间值的联动，例如这里删除了，中间值也要删除
      // props.removeQueryField(key);
    }
  });
  await props.loadTableList();
};
//自动提交表单，用于表单中某个元素值改变时使用
const autoSubmitQuery = () => {
  if (props.autoSubmit) {
    //如果允许自动提交
    submitQuery();
  }
};
// 清空查询条件并重置表格
const clearAndRereshTable = async () => {
  // 清空查询条件前要处理的钩子函数
  emit('beforeClearQuery');
  // 清空查询条件
  props.clearQuery();
  // 清空查询条件后要处理的钩子函数
  emit('afterClearQuery');
  // 重新刷新表格
  props.loadTableList();
};
</script>
<style lang="scss" scoped>
// .el-select {
//   // 重写element plus选择框的宽度，因此此处选择框的宽度无法正常显示.
//   //原因如下：在版本 2.5.0之后， el-select 的默认宽度更改为 100% 当使用内联形式时，宽度将显示异常。 为了保持显示正常, 您需要手动配置 el-select 的宽度 (如: 例子).
//   width: 200px;
// }
</style>
